import json
import os
import subprocess
import dnf
import dnf.package
import logging


common_arch = "noarch"


class Query:

    def __init__(self, confFile, arch, rpm_list):
        self._arch = arch
        self._rpm_list = rpm_list
        self._dnf_base = dnf.Base()
        self._dnf_base.conf.substitutions["arch"] = arch
        self._dnf_base.conf.read(confFile)
        self._dnf_base.conf.reposdir = ""

        self._logger = logging.getLogger()

    def __del__(self):
        self._dnf_base.close()

    def load_repo_data(self):
        self._dnf_base.read_all_repos()
        self._dnf_base.fill_sack(False, True)

    def query_rpm_in_filesystem(self, rootfs):
        command = "rpm -qa -qf '%%{NAME}\n' --root=%s" % rootfs
        ret = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE, universal_newlines=True).communicate()
        if len(ret[1]) > 0:
            self._logger.error(ret[1])
            return []
        return ret[0].splitlines()

    def query_depends(self, rpm_name, dependency_level):
        pkg = self._query_rpm(rpm_name)
        depends = set()

        if pkg is None:
            return set()

        if dependency_level == "require":
            capabilities = pkg.requires
        elif dependency_level == "recommends":
            capabilities = pkg.recommends
        else:
            return set()

        for capability in capabilities:
            providers = self.query_what_provide(str(capability))
            if providers is None:
                continue
            depends.add(providers[0].name)
        return depends

    def query_what_provide(self, capability):
        q = self._dnf_base.sack.query()
        q = q.filter(provides=capability, arch=[self._arch, common_arch])
        providers = list(q)
        if len(providers) == 0:
            self._logger.error("find no pakcage provides[%s]" % capability)
            return None
        if len(providers) == 1:
            return providers

        # here we still return all providers, and use rpm list in image to filter which pakcage to use
        self._logger.warn("find multiple pakcages provides [%s]" % capability)
        for provider in providers:
            self._logger.warn("pakcage: %s from repository %s" %
                              (provider, provider.reponame))
        return providers

    def query_what_depends(self, rpm_name, dependency_level):
        pkg = self._query_rpm(rpm_name)

        father = set()
        if pkg is None:
            return set()

        if dependency_level != "require" and dependency_level != "recommend":
            return set()

        for capability in pkg.provides:
            if dependency_level == "require":
                q = self._dnf_base.sack.query().filter(requires=capability, arch=[
                    self._arch, common_arch], name__neq=pkg.name)
            else:
                q = self._dnf_base.sack.query().filter(recommends=capability, arch=[
                    self._arch, common_arch], name__neq=pkg.name)
            for pkg in q:
                if pkg.name in self._rpm_list:
                    father.add(pkg.name)
        return father

    def _query_rpm(self, rpm_name):
        q = self._dnf_base.sack.query()

        q = q.filter(name=rpm_name, arch=[self._arch, common_arch])
        pkgs = list(q)

        if len(pkgs) == 0:
            self._logger.error(
                "find no package match the name [%s]" % rpm_name)
            return None

        if len(pkgs) == 1:
            return pkgs[0]

        candidate = str(pkgs[0])
        is_same = True
        for pkg in pkgs:
            if str(pkg) != candidate:
                is_same = False
                break

        if is_same:
            self._logger.warn(
                "pakcage same: find multiple packages match the name [%s]" % rpm_name)
            for pkg in pkgs:
                self._logger.warn(
                    "pakcage same: package: %s from repository: %s" % (pkg, pkg.reponame))
            return pkgs[0]

        self._logger.warn(
            "pakcage not same: find multiple packages match the name [%s]" % rpm_name)
        for pkg in pkgs:
            self._logger.warn(
                "pakcage not same: package: %s from repository: %s" % (pkg, pkg.reponame))
        return pkgs[0]
